package com.catl.plc.handler;

import com.catl.plc.master.AbstractPlcMaster;
import com.catl.plc.meta.*;
import com.catl.plc.utils.CollectionUtils;
import com.catl.plc.utils.Tool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author yanxinghua
 * @since 2021/5/13 10:58
 */
public abstract class PlcHandler {
    private final static Logger log = LoggerFactory.getLogger(PlcHandler.class);

    //********************************* writeRegister ***********************************************************************
    public static boolean writeRegister(AbstractPlcMaster abstractPlcMaster, RegisterTypeEnums registerType, int startOffset, Integer command) {
        return writeRegister(abstractPlcMaster, registerType, abstractPlcMaster.getSlaveId(),  startOffset,  command);
    }
    public static boolean writeRegisters(AbstractPlcMaster abstractPlcMaster, RegisterTypeEnums registerType, int startOffset, List<Integer> commands) {
        return writeRegisters(abstractPlcMaster, registerType, abstractPlcMaster.getSlaveId(),  startOffset,  commands);
    }
    public static boolean writeRegister(AbstractPlcMaster abstractPlcMaster, RegisterTypeEnums registerType, int slaveId, int startOffset, Integer command) {
        return writeRegisters(abstractPlcMaster,  registerType,  slaveId,  startOffset, Stream.of(command).collect(Collectors.toList()));
    }

    public static boolean writeRegisters(AbstractPlcMaster abstractPlcMaster, RegisterTypeEnums registerType, int slaveId, int startOffset, List<Integer> commands) {
        if (abstractPlcMaster.getType() == 1) {
            return Modbus4jUtil.write(abstractPlcMaster, registerType, slaveId, startOffset, commands);
        } else if (abstractPlcMaster.getType() == 2) {
            return Plc4jUtil.write(abstractPlcMaster, registerType, slaveId, startOffset, commands);
        }
        return false;
    }
    public static boolean writeEntity(AbstractPlcMaster abstractPlcMaster, ModbusEntity entity) {
        return writeEntity(abstractPlcMaster, abstractPlcMaster.getSlaveId(),  entity);
    }
    public static boolean writeEntity(AbstractPlcMaster abstractPlcMaster, int slaveId, ModbusEntity entity) {
        return writeEntities(abstractPlcMaster,  slaveId, Stream.of(entity).collect(Collectors.toList()));
    }
    public static boolean writeEntities(AbstractPlcMaster abstractPlcMaster, List<? extends ModbusEntity> entities) {
        return writeEntities(abstractPlcMaster,  abstractPlcMaster.getSlaveId(), entities);
    }
    /**
     * 写入
     *
     * @param slaveId
     * @param abstractPlcMaster
     * @param entities
     * @throws IllegalAccessException
     */
    public static boolean writeEntities(AbstractPlcMaster abstractPlcMaster, int slaveId, List<? extends ModbusEntity> entities) {
        try {
            return doWriteObjects(abstractPlcMaster, slaveId, entities);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }

    private static boolean doWriteObjects(AbstractPlcMaster abstractPlcMaster, int slaveId, List<? extends ModbusEntity> entities)
            throws IllegalAccessException {
        CollectionUtils.ClassWrapper preClazz = new CollectionUtils.ClassWrapper();
        CollectionUtils.ClassWrapper currentClazz = new CollectionUtils.ClassWrapper();
        AtomicInteger objectCount = new AtomicInteger(0);//连续出现相同的类实例
        for (int i = 0; i < entities.size(); i++) {
            ModbusEntity entity = entities.get(i);
            Class<? extends ModbusEntity> clazz = entity.getClass();
            currentClazz.setClazz(clazz);
            List<Field> fieldList = getEffectiveFields(clazz);
            List<List<Field>> equalFields = getEqualFields(fieldList);
            if (preClazz.getClazz() == currentClazz.getClazz()) {
                objectCount.incrementAndGet();
                if(!doSetObject(abstractPlcMaster, slaveId, entity, equalFields, objectCount)){
                    return false;
                }
                continue;
            }
            objectCount.set(0);
            preClazz.setClazz(currentClazz.getClazz());
            if(!doSetObject(abstractPlcMaster, slaveId, entity, equalFields, objectCount)){
                return false;
            }
        }
        return true;
    }

    private static boolean doSetObject(AbstractPlcMaster abstractPlcMaster, int slaveId, ModbusEntity entity, List<List<Field>> equalFields,
                                       AtomicInteger objectCount) throws IllegalAccessException {
        PlcInfo plcInfo = entity.getClass().getAnnotation(PlcInfo.class);
        RegisterTypeEnums registerType = plcInfo.registerType();
        int parentNo = plcInfo.parentNo();
        int size = plcInfo.size();
        int classLength = classLength(entity.getClass());
        List<Integer> commands = new ArrayList<>(classLength * size);
        int firstOffset = equalFields.get(0).get(0).getAnnotation(PlcField.class).offset();
        for (int i = 0; i < size; i++) {
            for (List<Field> fieldList : equalFields) {
                int fieldOffset = fieldList.get(0).getAnnotation(PlcField.class).offset();
                List<Integer> list = doWriteFields(entity, fieldList, objectCount);
                commands.addAll(fieldOffset - firstOffset + classLength * i, list);
            }
        }
        boolean result = writeRegisters(abstractPlcMaster, registerType, slaveId, firstOffset - parentNo + objectCount.get() * classLength, commands);
        doWriteLog(slaveId, getEffectiveFields(entity.getClass()), result, commands);
        return result;
    }


    private static List<Integer> doWriteFields(ModbusEntity entity, List<Field> equalOffsetFields, AtomicInteger objectCount)
            throws IllegalAccessException {
        if (CollectionUtils.isEmpty(equalOffsetFields)) {
            throw new NullPointerException("可操作的对象字段为空");
        }
        List<Integer> commands = doSetFieldData(entity, equalOffsetFields);
        return commands;
    }

    private static List<Integer> doSetFieldData(ModbusEntity entity, List<Field> fields) throws IllegalAccessException {
        if (CollectionUtils.isEmpty(fields)) {
            return new ArrayList<Integer>();
        }
        int bit = fields.stream().findFirst().get().getAnnotation(PlcField.class).bit();
        if (bit < 0) {
            if (fields.size() > 1) {
                throw new IllegalAccessException("非法数据");
            }
            Field field = fields.get(0);
            if (null == field.get(entity)) {
                return null;
            }
            return doSetShortDate(entity, field);
        } else {
            return doSetBitDate(entity, fields);
        }
    }

    private static List<Integer> doSetShortDate(ModbusEntity entity, Field field) throws IllegalAccessException {
        Class fieldType = field.getType();
        String fieldTypeName = fieldType.getSimpleName().toLowerCase();
        PlcField plcField = field.getAnnotation(PlcField.class);
        PlcValue[] plcValues = plcField.values();
        PlcOperate plcOperate = plcField.operate();
        String binStr = null;
        if (fieldTypeName.contains("byte")) {
            byte b = reOperateFieldValue((Number)field.get(entity), plcOperate).byteValue();
            binStr = Tool.shortToBinString(b, 8);
        } else if (fieldTypeName.contains("short")) {
            short b = reOperateFieldValue((Number)field.get(entity), plcOperate).shortValue();
            binStr = Tool.shortToBinString(b, 16);
        } else if (fieldTypeName.contains("int")) {
            int b = reOperateFieldValue((Number)field.get(entity), plcOperate).intValue();
            binStr = Tool.shortToBinString((short)b, 16);
        } else if (fieldTypeName.contains("long")) {
            long b = reOperateFieldValue((Number)field.get(entity), plcOperate).longValue();
            binStr = Tool.shortToBinString((int)b, 32);
        } else if (fieldTypeName.contains("float")) {
            float b = reOperateFieldValue((Number)field.get(entity), plcOperate).floatValue();
            binStr = Tool.shortToBinString((short)b, 16);
        } else if (fieldTypeName.contains("double")) {
            double b = reOperateFieldValue((Number)field.get(entity), plcOperate).doubleValue();
            binStr = Tool.shortToBinString((int)b, 32);
        } else if (fieldTypeName.contains("char")) {
            int b = reOperateFieldValue((Number) field.get(entity), plcOperate).intValue();
            binStr = Tool.shortToBinString((short)b, 16);
        } else if (fieldTypeName.contains("boolean")) {
            boolean b = field.getBoolean(entity);
            long b1 = Arrays.stream(plcValues)
                    .filter(e -> Boolean.parseBoolean(e.javaValue()) == b)
                    .map(e -> e.plcValue()).findAny().orElse(Long.MIN_VALUE);
            b1 = reOperateFieldValue(b1, plcOperate).longValue();
            binStr = Tool.shortToBinString((short)b1, 16);
        } else if (fieldTypeName.contains("decimal")) {
            BigDecimal b = reOperateFieldValue((BigDecimal) field.get(entity), plcOperate);
            binStr = b.toEngineeringString();
        } else {
            Object b = field.get(entity);
            binStr = String.valueOf(b);
        }
        return getFieldShortData(binStr);
    }

    public static void main(String[] args) {

        System.out.println((int) 34.23f);
    }

    private static List<Integer> doSetBitDate(ModbusEntity entity, List<Field> fields) throws IllegalAccessException {
        int len = fields.stream().filter(e -> null != e).findFirst().get().getAnnotation(PlcField.class).length();
        short[] shorts = new short[len * 16];
        for (int i = 0; i < fields.size(); i++) {
            Field f = fields.get(i);
            if (null == f || null == f.get(entity)) {
                continue;
            }
            String equalFieldTypeName = f.getType().getSimpleName().toLowerCase();
            PlcField equalPlcField = f.getAnnotation(PlcField.class);
            int equalBit = equalPlcField.bit();
            String valueStr = String.valueOf(f.get(entity));
            if (!"0".equals(valueStr) && !"1".equals(valueStr)) {
                throw new IllegalAccessException("二进制字符串只能包含 0 和 1 ！！！！！");
            }
            if ((equalFieldTypeName.contains("byte") || equalFieldTypeName.contains("short")
                    || equalFieldTypeName.contains("int") || equalFieldTypeName.contains("float")
                    || equalFieldTypeName.contains("char") || equalFieldTypeName.contains("number")
            ) && !equalFieldTypeName.contains("big")) {
                shorts[equalBit] = Short.parseShort(valueStr);
            } else if (equalFieldTypeName.contains("decimal")) {
                BigDecimal bigDecimal = (BigDecimal) f.get(entity);
                shorts[equalBit] = bigDecimal.shortValue();
            } else if (equalFieldTypeName.contains("bigint")) {
                BigInteger bigInteger = (BigInteger) f.get(entity);
                shorts[equalBit] = bigInteger.shortValue();
            } else if (equalFieldTypeName.contains("boolean")) {
                String equalFieldValue = String.valueOf(f.get(entity)).equals("true") ? "1" : "0";
                shorts[equalBit] = Short.parseShort(equalFieldValue);
            } else {
                String equalFieldValue = String.valueOf(f.get(entity));
                shorts[equalBit] = Short.parseShort(equalFieldValue);
            }
        }
        StringBuilder binStr = new StringBuilder("");
        for (short sh : shorts) {
            binStr.append(sh);
        }
        // 注意 这里把数组反转过来了，因为存储时 ，是高位存储低位数值（低值高存）
        return getFieldShortData(binStr.reverse().toString());
    }


//********************************* readRegister ***********************************************************************


    public static List<Integer> readRegister(AbstractPlcMaster abstractPlcMaster, RegisterTypeEnums registerType, int startOffset, int length) {
        return readRegister(abstractPlcMaster,  registerType, abstractPlcMaster.getSlaveId(), startOffset, length);
    }
    public static List<Integer> readRegister(AbstractPlcMaster abstractPlcMaster, RegisterTypeEnums registerType, int slaveId, int startOffset, int length) {
        List<Integer> result = new ArrayList<>();
        if (abstractPlcMaster.getType() == 1) {
            result = Modbus4jUtil.read(abstractPlcMaster, registerType, slaveId, startOffset, length);
        } else if (abstractPlcMaster.getType() == 2) {
            result = Plc4jUtil.read(abstractPlcMaster, registerType, slaveId, startOffset, length);
        }
        return result;
    }
    public static List<Integer> readByEntity(AbstractPlcMaster abstractPlcMaster, Class<? extends ModbusEntity> clazz) {
        return readByEntity(abstractPlcMaster, abstractPlcMaster.getSlaveId(),  clazz);
    }
    public static List<Integer> readByEntity(AbstractPlcMaster abstractPlcMaster, int slaveId, Class<? extends ModbusEntity> clazz) {
        PlcInfo plcInfo = clazz.getAnnotation(PlcInfo.class);
        int parentNo = plcInfo.parentNo();
        int offset = getEffectiveFields(clazz).get(0).getAnnotation(PlcField.class).offset();
        int size = plcInfo.size();
        int classLength = classLength(clazz);
        RegisterTypeEnums registerType = plcInfo.registerType();
        List<Integer> result = new ArrayList<>();
        if (abstractPlcMaster.getType() == 1) {
            result = Modbus4jUtil.read(abstractPlcMaster, registerType, slaveId, offset - parentNo, size * classLength);
        } else if (abstractPlcMaster.getType() == 2) {
            result = Plc4jUtil.read(abstractPlcMaster, registerType, slaveId, offset - parentNo, size * classLength);
        }
        return result;
    }

    public static List<Integer> getShortData(AbstractPlcMaster abstractPlcMaster, Class<? extends ModbusEntity> clazz) {
        return getShortData(abstractPlcMaster, abstractPlcMaster.getSlaveId(), clazz);
    }
    public static List<Integer> getShortData(AbstractPlcMaster abstractPlcMaster, int slaveId, Class<? extends ModbusEntity> clazz) {
        return readByEntity(abstractPlcMaster, slaveId, clazz);
    }

    public static List<char[]> getCharData(AbstractPlcMaster abstractPlcMaster, Class<? extends ModbusEntity> clazz) {
        return getCharData(abstractPlcMaster,  abstractPlcMaster.getSlaveId(), clazz);
    }

    public static List<char[]> getCharData(AbstractPlcMaster abstractPlcMaster, int slaveId, Class<? extends ModbusEntity> clazz) {
        return Tool.shorts2BinChars(getShortData(abstractPlcMaster, slaveId, clazz));
    }
    public static ModbusEntity readEntity(AbstractPlcMaster abstractPlcMaster, Class<? extends ModbusEntity> clazz) {
        return readEntity(abstractPlcMaster, abstractPlcMaster.getSlaveId(),  clazz);
    }

    public static ModbusEntity readEntity(AbstractPlcMaster abstractPlcMaster, int slaveId, Class<? extends ModbusEntity> clazz) {
        ModbusEntity entity = null;
        List<? extends ModbusEntity> list  = readEntities(abstractPlcMaster,slaveId, clazz);
        if(!CollectionUtils.isEmpty(list)){
            entity = list.get(0);
        }
        return entity;
    }

    public static List<? extends ModbusEntity> readEntities(AbstractPlcMaster abstractPlcMaster, Class<? extends ModbusEntity> clazz) {
        return readEntities(abstractPlcMaster, abstractPlcMaster.getSlaveId(), clazz);
    }
    /**
     * 读取成数据并转换成entity
     *
     * @param slaveId
     * @param clazz
     * @param abstractPlcMaster
     * @return
     */
    public static List<? extends ModbusEntity> readEntities(AbstractPlcMaster abstractPlcMaster, int slaveId, Class<? extends ModbusEntity> clazz) {
        List<? extends ModbusEntity> list = null;
        try {
            list = doSetObjectList(abstractPlcMaster, slaveId, clazz);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 创建对象集合
     */
    private static List<? extends ModbusEntity> doSetObjectList(AbstractPlcMaster abstractPlcMaster, int slaveId, Class<? extends ModbusEntity> clazz)
            throws InstantiationException, IllegalAccessException {
        PlcInfo plcInfo = clazz.getAnnotation(PlcInfo.class);
        int classLength = classLength(clazz);
//        int length = plcInfo.length();
//        if (length % classLength != 0) {
//            throw new IllegalArgumentException("读取的总长度必须是 class 长度的整倍数！！！！");
//        }
        List<char[]> allCharData = getCharData(abstractPlcMaster, slaveId, clazz);
        List<Field> fieldList = getEffectiveFields(clazz);
//        int ObjectCount = length / classLength;
        int ObjectCount = plcInfo.size();
        List<ModbusEntity> list = new ArrayList<>();
        for (int i = 0; i < ObjectCount; i++) {
            ModbusEntity entity = clazz.newInstance();
            List<char[]> ObjectCharData = allCharData.subList(i * classLength, (i + 1) * classLength);
            list.add(doSetObject(slaveId, entity, fieldList, ObjectCharData, i));
        }
        return list;
    }

    /**
     * 创建对象
     *
     * @param entity         对象
     * @param fieldList      对象字段
     * @param ObjectCharDate 所有字段值的集合
     * @param position       第几个对象,从零开始
     */
    private static ModbusEntity doSetObject(int slaveId, ModbusEntity entity, List<Field> fieldList, List<char[]> ObjectCharDate, int position) {
        int offset = getEffectiveFields(entity.getClass()).get(0).getAnnotation(PlcField.class).offset();
        fieldList.forEach(field -> {
            PlcField plcField = field.getAnnotation(PlcField.class);
            int fieldOffset = plcField.offset();
            int fieldLength = plcField.length();
            List<char[]> fieldCharList = ObjectCharDate.subList(fieldOffset - offset, fieldOffset - offset + fieldLength);
            StringBuilder stringBuilder = new StringBuilder("");
            for (char[] chars : fieldCharList) {
                stringBuilder.append(chars);
            }
            try {
                doSetField(slaveId, entity, field, stringBuilder.toString());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        return entity;
    }

    /**
     * 设置属性
     *
     * @param obj
     * @param field
     * @param filedValueStr
     * @throws IllegalAccessException
     */
    private static void doSetField(int slaveId, ModbusEntity obj, Field field, String filedValueStr)
            throws IllegalAccessException {
        PlcField plcField = field.getAnnotation(PlcField.class);
        String filedComment = plcField.comment();
        int fieldOffset = plcField.offset();
        int fieldLength = plcField.length();
        int fieldBit = plcField.bit();
        PlcValue[] fieldValues = plcField.values();
        boolean filedEnableLog = plcField.enableLog();
        PlcOperate plcOperate = plcField.operate();
        String typeName = field.getType().getTypeName().toLowerCase();
        if ((typeName.contains("long") || typeName.contains("double")) && filedValueStr.length() > 32) {
            throw new IllegalArgumentException("数据类型是 long 或是 double 时，寄存器数量不得超过 2个 ！！！！");
        }
        if ((typeName.contains("byte")
                || typeName.contains("short")
                || typeName.contains("int")
                || typeName.contains("float")
                || typeName.contains("char")
                || typeName.contains("boolean")
        ) && filedValueStr.length() > 16 && !typeName.contains("big")) {
            throw new IllegalArgumentException("数据类型是 byte、short、int、float、char、boolean 时，寄存器数量不得超过 1个 ！！！！");
        }
        PlcValue plcValue = null;
        if (fieldValues.length <= 0) {
            //不存在 fieldValues 时
            if (!typeName.contains("byte") && !typeName.contains("short") && !typeName.contains("int")
                    && !typeName.contains("long") && !typeName.contains("float") && !typeName.contains("double")
                    && !typeName.contains("decimal") && !typeName.contains("number")
            ) {
                throw new IllegalArgumentException("@PlcField 的 values() 不存在时，字段数据类型必须是数字类型！！！！");
            }
            if (fieldLength > 0 && fieldBit < 0) {
                plcValue = setPlcValue(" ", Long.valueOf(filedValueStr, 2), String.valueOf(Long.valueOf(filedValueStr, 2)));
            } else if (fieldLength > 0 && fieldBit >= 0) {
                if (filedValueStr.length() < fieldBit) {
                    throw new IndexOutOfBoundsException("读取的bit位超出数据长度");
                }
                String javaValue = filedValueStr.substring(filedValueStr.length() - 1 - fieldBit, filedValueStr.length() - fieldBit);
                plcValue = setPlcValue(filedComment, Long.parseLong(javaValue), javaValue);
            } else {
                throw new NullPointerException("fieldLength 必须存在 ！！！！");
            }
        } else {
            // 存在 fieldValues 时
            if (fieldLength > 0 && fieldBit < 0) {
                plcValue = Arrays.stream(fieldValues).filter(f -> f.plcValue() == Long.valueOf(filedValueStr, 2))
                        .findFirst().orElse(defaultPlcValue());
            } else if (fieldLength > 0 && fieldBit >= 0) {
                if (filedValueStr.length() < fieldBit) {
                    throw new IndexOutOfBoundsException("读取的bit位超出数据长度");
                }
                plcValue = Arrays.stream(fieldValues).filter(f ->
                        f.plcValue() == Long.parseLong(filedValueStr.substring(filedValueStr.length() - 1 - fieldBit,
                                filedValueStr.length() - fieldBit))
                ).findFirst().orElse(defaultPlcValue());
            } else {
                throw new NullPointerException("fieldLength 必须存在 ！！！！");
            }
        }
        doSetFieldValue(obj, field, plcValue, plcOperate);
        if (filedEnableLog) {
            log.warn("【读取 PLC】: {} ---> 从机号:{} , {}({}) ，{}({} --> {})", "true", slaveId, filedComment, fieldOffset + (fieldBit < 0 ? "" : " ; 第 " + fieldBit + " 位 bit"),
                    plcValue.comment(), plcValue.plcValue(), Long.toBinaryString(plcValue.plcValue()));
        }
    }

    /**
     * 设置字段值
     *
     * @param readHandle
     * @param field
     * @param fieldValue
     * @throws IllegalAccessException
     */
    private static void doSetFieldValue(ModbusEntity readHandle, Field field, PlcValue fieldValue, PlcOperate plcOperate)
            throws IllegalAccessException {
        String type = field.getType().toString().toLowerCase();
        BigDecimal result = operateFieldValue(fieldValue, plcOperate);
        if (type.contains("byte")) {
            field.set(readHandle, result.byteValue());
        } else if (type.contains("short")) {
            field.set(readHandle, result.shortValue());
        } else if (type.contains("int") && !type.contains("big")) {
            field.set(readHandle, result.intValue());
        } else if (type.contains("long")) {
            field.set(readHandle, result.longValue());
        } else if (type.contains("float")) {
            field.set(readHandle, result.floatValue());
        } else if (type.contains("double")) {
            field.set(readHandle, result.doubleValue());
        } else if (type.contains("char")) {
            field.set(readHandle, (char) result.intValue());
        } else if (type.contains("boolean")) {
            String value = fieldValue.javaValue();
            if (fieldValue.plcValue() < 0) {
                value = "false";
            }
            field.setBoolean(readHandle, Boolean.valueOf(value));
        } else if (type.contains("decimal")) {
            field.set(readHandle, result);
        } else if (type.contains("bigint")) {
            field.set(readHandle, result.toBigInteger());
        } else {
            field.set(readHandle, fieldValue.javaValue());
        }
    }

//********************************* other ***********************************************************************

    /**
     * 读取日志
     *
     * @param fields
     * @param result
     * @param shorts
     */
    private static void doReadLog(List<Field> fields, boolean result, short[] shorts) {

    }

    /**
     * 下控日志
     *
     * @param fields
     * @param result
     * @param shorts
     */
    private static void doWriteLog(int slaveId, List<Field> fields, boolean result, List<Integer> shorts) {
        PlcInfo plcInfo = fields.get(0).getDeclaringClass().getAnnotation(PlcInfo.class);
        int size = plcInfo.size();
        shorts = shorts.subList(0, shorts.size() / size);
        int firstOffset = fields.get(0).getAnnotation(PlcField.class).offset();
        for (Field field : fields) {
            PlcField plcField = field.getAnnotation(PlcField.class);
            String comment = plcField.comment();
            int offset = plcField.offset();
            int bit = plcField.bit();
            int length = plcField.length();
            PlcValue[] plcValues = plcField.values();
            PlcOperate operate = plcField.operate();
            boolean enableLog = plcField.enableLog();
            List<Integer> rs = shorts.subList(offset - firstOffset, offset - firstOffset + length);
            if (enableLog) {
                log.warn("【写入 PLC】: {} ---> 从机号:{} , {}({}) , {}({})", result ? "成功" : "失败", slaveId, comment, offset + (bit < 0 ? "" : " ; 第 " + bit + " 位bit"),
                        rs, Tool.shorts2BinStr(rs));
            }
        }
    }

    /**
     * 把字段归类处理 ： offset相同，bit小于0 的字段放在一个集合里；offset相同，bit大于等于0 的字段放在一个集合里；然后合在一起
     *
     * @param fieldList
     * @return
     */
    private static List<List<Field>> getEqualFields(List<Field> fieldList) {
        List<List<Field>> result = new ArrayList<>();
        fieldList.parallelStream()
                .collect(Collectors.groupingBy(e -> Integer.valueOf(e.getAnnotation(PlcField.class).offset())))
                .values().stream()
                .sorted((field1, field2) -> {
                    if (field1.get(0).getAnnotation(PlcField.class).offset() > field2.get(0).getAnnotation(PlcField.class).offset()) {
                        return 1;
                    }
                    return -1;
                }).forEach(v -> {
            List<Field> byteList = v.parallelStream().filter(e -> e.getAnnotation(PlcField.class).bit() < 0).collect(Collectors.toList());
            List<Field> bitList = v.parallelStream().filter(e -> e.getAnnotation(PlcField.class).bit() >= 0).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(byteList)) {
                result.add(byteList);
            }
            if (!CollectionUtils.isEmpty(bitList)) {
                result.add(bitList);
            }
        });
        return result;
    }

    /**
     * 获取使用 @PlcField 注解的字段
     *
     * @param clazz
     * @return
     */
    private static List<Field> getEffectiveFields(Class<? extends ModbusEntity> clazz) {
        List<Field> fieldList = Arrays.stream(clazz.getDeclaredFields()).parallel()
                .filter(field -> field.isAnnotationPresent(PlcField.class))
                .sorted((field1, field2) -> {
                    if (field1.getAnnotation(PlcField.class).offset() > field2.getAnnotation(PlcField.class).offset()) {
                        return 1;
                    }
                    return -1;
                }).peek(e -> e.setAccessible(true)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(fieldList)) {
            throw new NullPointerException("标有 @PlcField 注解的字段 必须存在 ！！！！");
        }
        return fieldList;
    }

    private static List<Integer> getFieldShortData(String binStr) {
        List<Integer> intList = new ArrayList<>();
        if (binStr.length() < 32) {
            intList = Tool.asList(Integer.valueOf(binStr, 2));
        } else {
            intList = Tool.splitStrByLength(binStr, 16).stream()
                    .map(e -> Integer.valueOf(e, 2))
                    .collect(Collectors.toList());
        }
        return intList;
    }


    private static BigDecimal operateFieldValue(PlcValue fieldValue, PlcOperate plcOperate) {
        long plcValue = fieldValue.plcValue();
        BigDecimal bigDecimal = new BigDecimal(String.valueOf(plcValue));
        double beHandle = plcOperate.beHandle();
        BigDecimal result = null;
        switch (plcOperate.handle()) {
            case ADD:
                result = bigDecimal.add(new BigDecimal(String.valueOf(beHandle)));
                break;
            case SUB:
                result = bigDecimal.subtract(new BigDecimal(String.valueOf(beHandle)));
                break;
            case MUL:
                result = bigDecimal.multiply(new BigDecimal(String.valueOf(beHandle)));
                break;
            case DIV:
                result = bigDecimal.divide(new BigDecimal(String.valueOf(beHandle)));
                break;
            default:
                result = bigDecimal;
                break;
        }
        return result;
    }

    private static BigDecimal reOperateFieldValue(Number number, PlcOperate plcOperate) {
        BigDecimal bigDecimal = new BigDecimal(String.valueOf(number));
        double beHandle = plcOperate.beHandle();
        BigDecimal result = null;
        switch (plcOperate.handle()) {
            case ADD:
                result = bigDecimal.subtract(new BigDecimal(String.valueOf(beHandle)));
                break;
            case SUB:
                result = bigDecimal.add(new BigDecimal(String.valueOf(beHandle)));
                break;
            case MUL:
                result = bigDecimal.divide(new BigDecimal(String.valueOf(beHandle)));
                break;
            case DIV:
                result = bigDecimal.multiply(new BigDecimal(String.valueOf(beHandle)));
                break;
            default:
                result = bigDecimal;
                break;
        }
        return result;
    }

    private static PlcValue defaultPlcValue() {
        return new PlcValue() {
            @Override
            public Class<? extends Annotation> annotationType() {
                return PlcValue.class;
            }

            @Override
            public String comment() {
                return "无效值";
            }

            @Override
            public long plcValue() {
                return -1;
            }

            @Override
            public String javaValue() {
                return "-1";
            }
        };
    }

    private static PlcValue setPlcValue(String filedComment, long plcValue, String javaValue) {
        return new PlcValue() {
            @Override
            public Class<? extends Annotation> annotationType() {
                return PlcValue.class;
            }

            @Override
            public String comment() {
                return filedComment;
            }

            @Override
            public long plcValue() {
                return plcValue;
            }

            @Override
            public String javaValue() {
                return javaValue;
            }
        };
    }

    private static int classLength(Class clazz) {
        List<Field> list = getEffectiveFields(clazz);
        int min = list.get(0).getAnnotation(PlcField.class).offset();
        int max = list.get(list.size() - 1).getAnnotation(PlcField.class).offset() + list.get(list.size() - 1).getAnnotation(PlcField.class).length() - 1;
        return max - min + 1;
    }
}
